/*
 * GetWebInfo.c
 *
 *  Created on: 2010-11-12
 *      Author: fufan
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>


#define BUFSIZE	10000

const char *WEBPORT = "80";

void DieWithUserMessage(const char *msg, const char *detail);
void DieWithSystemMessage(const char *msg);
void usage();
struct addrinfo* getHostIp(const char *host); //根据host返回地址信息链
int buildConnect(struct addrinfo *servIp); //建立TCP连接
void sendrecv(int sock, char *getWebInfo);//发送HTTP GET后接受服务器返回的消息


int main(int argc, char *argv[])
{
			if(argc != 2)
			{
				usage();
				exit(1);
			}

			/*根据HTTP协议发送指定的数据到服务器端*/
			char getWebInfo[BUFSIZE] = "";
			strcat(getWebInfo, "GET / HTTP/1.1\r\n");
			strcat(getWebInfo, "Host:");
			strcat(getWebInfo, argv[1]);
			strcat(getWebInfo, "\r\n");
			strcat(getWebInfo, "User-Agent: HTMLGET 1.1\r\n\r\n");

			/*根据地址信息建立连接*/
			struct addrinfo* servIp = getHostIp(argv[1]);
			//建立tcp连接
			int sock = -1;
			sock = buildConnect(servIp);
			if(sock < 0)
			{
					DieWithSystemMessage("buildConnect() failed!");
			}

			sendrecv(sock, getWebInfo);


				close(sock);
				exit(0);
}

void DieWithUserMessage(const char *msg, const char *detail)
{
			  fputs(msg, stderr);
			  fputs(": ", stderr);
			  fputs(detail, stderr);
			  fputs("\n", stderr);
			  exit(1);
}

void DieWithSystemMessage(const char *msg)
{
				perror(msg);
				exit(1);
}

void usage()
{
			fprintf(stderr, "USAGE: GetWebInfo host port\n\
								\t host: the website hostname. ex:www.ifeng.com");
}

/*根据host返回地址信息链表*/
struct addrinfo* getHostIp(const char *host)
{
					struct addrinfo addrCriteria;
					memset(&addrCriteria, 0, sizeof(addrCriteria));
					addrCriteria.ai_family = AF_UNSPEC;
					addrCriteria.ai_protocol = IPPROTO_TCP;
					addrCriteria.ai_socktype = SOCK_STREAM;


					struct addrinfo *hostIp;
					int rtnVal = getaddrinfo(host, WEBPORT, &addrCriteria, &hostIp);
					if(rtnVal !=0)
					{
							DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnVal));
					}
					return hostIp;
}


/*遍历servIp链表，试图找到可行的地址信息，并且尝试连接*/
int buildConnect(struct addrinfo *servIp)
{
				int sock = -1;

				struct addrinfo *addr;
				for(addr=servIp; servIp != NULL; servIp=servIp->ai_next)
				{
						sock = socket(servIp->ai_family, servIp->ai_socktype, servIp->ai_protocol);
						if(sock < 0)
						{
									continue;
						}
						//连接上一个可行地址后立即跳出循环返回相应的socket句柄
						if(connect(sock, servIp->ai_addr, servIp->ai_addrlen) == 0)
						{
									break;
						}
						close(sock);
						sock = -1;
				}
				freeaddrinfo(servIp);
				return sock;
}

/*发送HTTP GET后接受服务器返回的消息*/
void sendrecv(int sock, char *getWebInfo)
{
			size_t stringLen = strlen(getWebInfo);

			//发送信息
			ssize_t numBytes = send(sock, getWebInfo, stringLen, 0);
			if(numBytes < 0)
			{
						DieWithSystemMessage("send() failed!");
			}
			else if(numBytes != stringLen)
			{
						DieWithUserMessage("send() failed!", "send unexpect bytes!");
			}

				char buffer[BUFSIZE];
				memset(buffer, 0, sizeof(buffer));
				ssize_t numRcvd;
				int htmlStart = 0;
				char *htmlContent;

				//接受信息
				while((numRcvd=recv(sock, buffer, BUFSIZE-1, 0)) >  0)
				{
					buffer[numRcvd] = '\0';

					//首先需要找到"\r\n\r\n"之后的字符串进行输出
					if(htmlStart == 0)
					{
						if((htmlContent = strstr(buffer, "\r\n\r\n")) != NULL)
							{
									htmlStart =1;
									htmlContent += 4;
							}
					}
					else
					{
							htmlContent = buffer;
					}

					//如果找到网页内容的开始处，则输出，否则什么都不做。
					if(htmlStart)
					{
						fputs(htmlContent, stdout);
					}
					memset(buffer, 0, numRcvd);
				}

				if(numRcvd < 0)
				{
					perror("error receving data!");
				}
}
